home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / Python 1.3.3 / Python 133 SRC / Mac / Demo / waste / swed.py < prev    next >
Text File  |  1996-04-23  |  16KB  |  629 lines

  1. # A minimal text editor.
  2. #
  3. # To be done:
  4. # - Functionality: find, etc.
  5.  
  6. from Menu import DrawMenuBar
  7. from FrameWork import *
  8. import Win
  9. import Qd
  10. import Res
  11. import Fm
  12. import waste
  13. import WASTEconst
  14. import Scrap
  15. import os
  16. import macfs
  17.  
  18. UNDOLABELS = [ # Indexed by WEGetUndoInfo() value
  19.     None, "", "typing", "Cut", "Paste", "Clear", "Drag", "Style"]
  20.     
  21. # Style and size menu. Note that style order is important (tied to bit values)
  22. STYLES = [
  23.     ("Bold", "B"), ("Italic", "I"), ("Underline", "U"), ("Outline", "O"),
  24.     ("Shadow", ""), ("Condensed", ""), ("Extended", "")
  25.     ]
  26. SIZES = [ 9, 10, 12, 14, 18, 24]
  27.     
  28. BIGREGION=Qd.NewRgn()
  29. Qd.SetRectRgn(BIGREGION, -16000, -16000, 16000, 16000)
  30.  
  31. class WasteWindow(ScrolledWindow):
  32.     def open(self, path, name, data):
  33.         self.path = path
  34.         self.name = name
  35.         r = windowbounds(400, 400)
  36.         w = Win.NewWindow(r, name, 1, 0, -1, 1, 0x55555555)
  37.         self.wid = w
  38.         vr = 0, 0, r[2]-r[0]-15, r[3]-r[1]-15
  39.         dr = (0, 0, 10240, 0)
  40.         Qd.SetPort(w)
  41.         Qd.TextFont(4)
  42.         Qd.TextSize(9)
  43.         flags = WASTEconst.weDoAutoScroll | WASTEconst.weDoOutlineHilite | \
  44.             WASTEconst.weDoMonoStyled | WASTEconst.weDoUndo
  45.         self.ted = waste.WENew(dr, vr, flags)
  46.         style, soup = self.getstylesoup()
  47.         self.ted.WEInsert(data, style, soup)
  48.         self.ted.WESetSelection(0,0)
  49.         self.ted.WECalText()
  50.         self.ted.WEResetModCount()
  51.         w.DrawGrowIcon()
  52.         self.scrollbars()
  53.         self.do_postopen()
  54.         self.do_activate(1, None)
  55.         
  56.     def getstylesoup(self):
  57.         if not self.path:
  58.             return None, None
  59.         oldrf = Res.CurResFile()
  60.         try:
  61.             rf = Res.OpenResFile(self.path)
  62.         except Res.Error:
  63.             return None, None
  64.         try:
  65.             hstyle = Res.Get1Resource('styl', 128)
  66.             hstyle.DetachResource()
  67.         except Res.Error:
  68.             hstyle = None
  69.         try:
  70.             hsoup = Res.Get1Resource('SOUP', 128)
  71.             hsoup.DetachResource()
  72.         except Res.Error:
  73.             hsoup = None
  74.         Res.CloseResFile(rf)
  75.         Res.UseResFile(oldrf)
  76.         return hstyle, hsoup
  77.                 
  78.     def do_idle(self, event):
  79.         (what, message, when, where, modifiers) = event
  80.         Qd.SetPort(self.wid)
  81.         self.ted.WEIdle()    
  82.         if self.ted.WEAdjustCursor(where, BIGREGION):
  83.             return
  84.         Qd.SetCursor(Qd.qd.arrow)
  85.         
  86.     def getscrollbarvalues(self):
  87.         dr = self.ted.WEGetDestRect()
  88.         vr = self.ted.WEGetViewRect()
  89.         vx = self.scalebarvalue(dr[0], dr[2], vr[0], vr[2])
  90.         vy = self.scalebarvalue(dr[1], dr[3], vr[1], vr[3])
  91.         return vx, vy
  92.         
  93.     def scrollbar_callback(self, which, what, value):
  94.         if which == 'y':
  95.             if what == 'set':
  96.                 height = self.ted.WEGetHeight(0, 0x3fffffff)
  97.                 cur = self.getscrollbarvalues()[1]
  98.                 delta = (cur-value)*height/32767
  99.             if what == '-':
  100.                 topline_off,dummy = self.ted.WEGetOffset((1,1))
  101.                 topline_num = self.ted.WEOffsetToLine(topline_off)
  102.                 delta = self.ted.WEGetHeight(topline_num, topline_num+1)
  103.             elif what == '--':
  104.                 delta = (self.ted.WEGetViewRect()[3]-10)
  105.                 if delta <= 0:
  106.                     delta = 10 # Random value
  107.             elif what == '+':
  108.                 # XXXX Wrong: should be bottom line size
  109.                 topline_off,dummy = self.ted.WEGetOffset((1,1))
  110.                 topline_num = self.ted.WEOffsetToLine(topline_off)
  111.                 delta = -self.ted.WEGetHeight(topline_num, topline_num+1)
  112.             elif what == '++':
  113.                 delta = -(self.ted.WEGetViewRect()[3]-10)
  114.                 if delta >= 0:
  115.                     delta = -10
  116.             self.ted.WEScroll(0, delta)
  117.         else:
  118.             if what == 'set':
  119.                 return # XXXX
  120.             vr = self.ted.WEGetViewRect()
  121.             winwidth = vr[2]-vr[0]
  122.             if what == '-':
  123.                 delta = winwidth/10
  124.             elif what == '--':
  125.                 delta = winwidth/2
  126.             elif what == '+':
  127.                 delta = -winwidth/10
  128.             elif what == '++':
  129.                 delta = -winwidth/2
  130.             self.ted.WEScroll(delta, 0)
  131.         # Pin the scroll
  132.         l, t, r, b = self.ted.WEGetDestRect()
  133.         vl, vt, vr, vb = self.ted.WEGetViewRect()
  134.         if t > 0 or l > 0:
  135.             dx = dy = 0
  136.             if t > 0: dy = -t
  137.             if l > 0: dx = -l
  138.             self.ted.WEScroll(dx, dy)
  139.         elif b < vb:
  140.             self.ted.WEScroll(0, b-vb)
  141.  
  142.         
  143.     def do_activate(self, onoff, evt):
  144.         Qd.SetPort(self.wid)
  145.         ScrolledWindow.do_activate(self, onoff, evt)
  146.         if onoff:
  147.             self.ted.WEActivate()
  148.             self.parent.active = self
  149.             self.parent.updatemenubar()
  150.         else:
  151.             self.ted.WEDeactivate()
  152.  
  153.     def do_update(self, wid, event):
  154.         region = wid.GetWindowPort().visRgn
  155.         if Qd.EmptyRgn(region):
  156.             return
  157.         Qd.EraseRgn(region)
  158.         self.ted.WEUpdate(region)
  159.         self.updatescrollbars()
  160.         
  161.     def do_postresize(self, width, height, window):
  162.         l, t, r, b = self.ted.WEGetViewRect()
  163.         vr = (l, t, l+width-15, t+height-15)
  164.         self.ted.WESetViewRect(vr)
  165.         Win.InvalRect(vr)
  166.         ScrolledWindow.do_postresize(self, width, height, window)
  167.         
  168.     def do_contentclick(self, local, modifiers, evt):
  169.         (what, message, when, where, modifiers) = evt
  170.         self.ted.WEClick(local, modifiers, when)
  171.         self.updatescrollbars()
  172.         self.parent.updatemenubar()
  173.  
  174.     def do_char(self, ch, event):
  175.         self.ted.WESelView()
  176.         (what, message, when, where, modifiers) = event
  177.         self.ted.WEKey(ord(ch), modifiers)
  178.         self.updatescrollbars()
  179.         self.parent.updatemenubar()
  180.         
  181.     def close(self):
  182.         if self.ted.WEGetModCount():
  183.             save = EasyDialogs.AskYesNoCancel('Save window "%s" before closing?'%self.name, 1)
  184.             if save > 0:
  185.                 self.menu_save()
  186.             elif save < 0:
  187.                 return
  188.         if self.parent.active == self:
  189.             self.parent.active = None
  190.         self.parent.updatemenubar()
  191.         del self.ted
  192.         self.do_postclose()
  193.         
  194.     def menu_save(self):
  195.         if not self.path:
  196.             self.menu_save_as()
  197.             return # Will call us recursively
  198.         #
  199.         # First save data
  200.         #
  201.         dhandle = self.ted.WEGetText()
  202.         data = dhandle.data
  203.         fp = open(self.path, 'wb')  # NOTE: wb, because data has CR for end-of-line
  204.         fp.write(data)
  205.         if data[-1] <> '\r': fp.write('\r')
  206.         fp.close()
  207.         #
  208.         # Now save style and soup
  209.         #
  210.         oldresfile = Res.CurResFile()
  211.         try:
  212.             rf = Res.OpenResFile(self.path)
  213.         except Res.Error:
  214.             Res.CreateResFile(self.path)
  215.             rf = Res.OpenResFile(self.path)
  216.         styles = Res.Resource('')
  217.         soup = Res.Resource('')
  218.         self.ted.WECopyRange(0, 0x3fffffff, None, styles, soup)
  219.         styles.AddResource('styl', 128, '')
  220.         soup.AddResource('SOUP', 128, '')
  221.         Res.CloseResFile(rf)
  222.         Res.UseResFile(oldresfile)
  223.         
  224.         self.ted.WEResetModCount()
  225.         
  226.     def menu_save_as(self):
  227.         fss, ok = macfs.StandardPutFile('Save as:')
  228.         if not ok: return
  229.         self.path = fss.as_pathname()
  230.         self.name = os.path.split(self.path)[-1]
  231.         self.wid.SetWTitle(self.name)
  232.         self.menu_save()
  233.         
  234.     def menu_cut(self):
  235.         self.ted.WESelView()
  236.         self.ted.WECut()
  237.         Scrap.ZeroScrap()
  238.         self.ted.WECut()
  239.         self.updatescrollbars()
  240.         self.parent.updatemenubar()
  241.         
  242.     def menu_copy(self):
  243.         Scrap.ZeroScrap()
  244.         self.ted.WECopy()
  245.         self.updatescrollbars()
  246.         self.parent.updatemenubar()
  247.         
  248.     def menu_paste(self):
  249.         self.ted.WESelView()
  250.         self.ted.WEPaste()
  251.         self.updatescrollbars()
  252.         self.parent.updatemenubar()
  253.         
  254.     def menu_clear(self):
  255.         self.ted.WESelView()
  256.         self.ted.WEDelete()
  257.         self.updatescrollbars()
  258.         self.parent.updatemenubar()
  259.  
  260.     def menu_undo(self):
  261.         self.ted.WEUndo()
  262.         self.updatescrollbars()
  263.         self.parent.updatemenubar()
  264.         
  265.     def menu_setfont(self, font):
  266.         font = Fm.GetFNum(font)
  267.         self.mysetstyle(WASTEconst.weDoFont, (font, 0, 0, (0,0,0)))
  268.         self.parent.updatemenubar()
  269.                 
  270.     def menu_modface(self, face):
  271.         self.mysetstyle(WASTEconst.weDoFace|WASTEconst.weDoToggleFace, 
  272.             (0, face, 0, (0,0,0)))
  273.  
  274.     def menu_setface(self, face):
  275.         self.mysetstyle(WASTEconst.weDoFace|WASTEconst.weDoReplaceFace, 
  276.             (0, face, 0, (0,0,0)))
  277.  
  278.     def menu_setsize(self, size):
  279.         self.mysetstyle(WASTEconst.weDoSize, (0, 0, size, (0,0,0)))
  280.                                 
  281.     def menu_incsize(self, size):
  282.         self.mysetstyle(WASTEconst.weDoAddSize, (0, 0, size, (0,0,0)))
  283.  
  284.     def mysetstyle(self, which, how):
  285.         self.ted.WESelView()
  286.         self.ted.WESetStyle(which, how)
  287.         self.parent.updatemenubar()
  288.                                 
  289.     def have_selection(self):
  290.         start, stop = self.ted.WEGetSelection()
  291.         return start < stop
  292.         
  293.     def can_paste(self):
  294.         return self.ted.WECanPaste()
  295.         
  296.     def can_undo(self):
  297.         which, redo = self.ted.WEGetUndoInfo()
  298.         which = UNDOLABELS[which]
  299.         if which == None: return None
  300.         if redo:
  301.             return "Redo "+which
  302.         else:
  303.             return "Undo "+which
  304.             
  305.     def getruninfo(self):
  306.         all = (WASTEconst.weDoFont | WASTEconst.weDoFace | WASTEconst.weDoSize)
  307.         dummy, mode, (font, face, size, color) = self.ted.WEContinuousStyle(all)
  308.         if not (mode & WASTEconst.weDoFont):
  309.             font = None
  310.         else:
  311.             font = Fm.GetFontName(font)
  312.         if not (mode & WASTEconst.weDoFace): fact = None
  313.         if not (mode & WASTEconst.weDoSize): size = None
  314.         return font, face, size
  315.  
  316. class Wed(Application):
  317.     def __init__(self):
  318.         Application.__init__(self)
  319.         self.num = 0
  320.         self.active = None
  321.         self.updatemenubar()
  322.         waste.STDObjectHandlers()
  323.         
  324.     def makeusermenus(self):
  325.         self.filemenu = m = Menu(self.menubar, "File")
  326.         self.newitem = MenuItem(m, "New window", "N", self.open)
  327.         self.openitem = MenuItem(m, "Open...", "O", self.openfile)
  328.         self.closeitem = MenuItem(m, "Close", "W", self.closewin)
  329.         m.addseparator()
  330.         self.saveitem = MenuItem(m, "Save", "S", self.save)
  331.         self.saveasitem = MenuItem(m, "Save as...", "", self.saveas)
  332.         m.addseparator()
  333.         self.quititem = MenuItem(m, "Quit", "Q", self.quit)
  334.         
  335.         self.editmenu = m = Menu(self.menubar, "Edit")
  336.         self.undoitem = MenuItem(m, "Undo", "Z", self.undo)
  337.         self.cutitem = MenuItem(m, "Cut", "X", self.cut)
  338.         self.copyitem = MenuItem(m, "Copy", "C", self.copy)
  339.         self.pasteitem = MenuItem(m, "Paste", "V", self.paste)
  340.         self.clearitem = MenuItem(m, "Clear", "", self.clear)
  341.         
  342.         self.makefontmenu()
  343.         
  344.         # Groups of items enabled together:
  345.         self.windowgroup = [self.closeitem, self.saveitem, self.saveasitem,
  346.             self.editmenu, self.fontmenu, self.facemenu, self.sizemenu]
  347.         self.focusgroup = [self.cutitem, self.copyitem, self.clearitem]
  348.         self.windowgroup_on = -1
  349.         self.focusgroup_on = -1
  350.         self.pastegroup_on = -1
  351.         self.undo_label = "never"
  352.         self.ffs_values = ()
  353.         
  354.     def makefontmenu(self):
  355.         self.fontmenu = Menu(self.menubar, "Font")
  356.         self.fontnames = getfontnames()
  357.         self.fontitems = []
  358.         for n in self.fontnames:
  359.             m = MenuItem(self.fontmenu, n, "", self.selfont)
  360.             self.fontitems.append(m)
  361.         self.facemenu = Menu(self.menubar, "Style")
  362.         self.faceitems = []
  363.         for n, shortcut in STYLES:
  364.             m = MenuItem(self.facemenu, n, shortcut, self.selface)
  365.             self.faceitems.append(m)
  366.         self.facemenu.addseparator()
  367.         self.faceitem_normal = MenuItem(self.facemenu, "Normal", "N", 
  368.             self.selfacenormal)
  369.         self.sizemenu = Menu(self.menubar, "Size")
  370.         self.sizeitems = []
  371.         for n in SIZES:
  372.             m = MenuItem(self.sizemenu, `n`, "", self.selsize)
  373.             self.sizeitems.append(m)
  374.         self.sizemenu.addseparator()
  375.         self.sizeitem_bigger = MenuItem(self.sizemenu, "Bigger", "+", 
  376.             self.selsizebigger)
  377.         self.sizeitem_smaller = MenuItem(self.sizemenu, "Smaller", "-", 
  378.             self.selsizesmaller)
  379.                     
  380.     def selfont(self, id, item, *rest):
  381.         if self.active:
  382.             font = self.fontnames[item-1]
  383.             self.active.menu_setfont(font)
  384.         else:
  385.             EasyDialogs.Message("No active window?")
  386.  
  387.     def selface(self, id, item, *rest):
  388.         if self.active:
  389.             face = (1<<(item-1))
  390.             self.active.menu_modface(face)
  391.         else:
  392.             EasyDialogs.Message("No active window?")
  393.  
  394.     def selfacenormal(self, *rest):
  395.         if self.active:
  396.             self.active.menu_setface(0)
  397.         else:
  398.             EasyDialogs.Message("No active window?")
  399.  
  400.     def selsize(self, id, item, *rest):
  401.         if self.active:
  402.             size = SIZES[item-1]
  403.             self.active.menu_setsize(size)
  404.         else:
  405.             EasyDialogs.Message("No active window?")
  406.  
  407.     def selsizebigger(self, *rest):
  408.         if self.active:
  409.             self.active.menu_incsize(2)
  410.         else:
  411.             EasyDialogs.Message("No active window?")
  412.  
  413.     def selsizesmaller(self, *rest):
  414.         if self.active:
  415.             self.active.menu_incsize(-2)
  416.         else:
  417.             EasyDialogs.Message("No active window?")
  418.  
  419.     def updatemenubar(self):
  420.         changed = 0
  421.         on = (self.active <> None)
  422.         if on <> self.windowgroup_on:
  423.             for m in self.windowgroup:
  424.                 m.enable(on)
  425.             self.windowgroup_on = on
  426.             changed = 1
  427.         if on:
  428.             # only if we have an edit menu
  429.             on = self.active.have_selection()
  430.             if on <> self.focusgroup_on:
  431.                 for m in self.focusgroup:
  432.                     m.enable(on)
  433.                 self.focusgroup_on = on
  434.                 changed = 1
  435.             on = self.active.can_paste()
  436.             if on <> self.pastegroup_on:
  437.                 self.pasteitem.enable(on)
  438.                 self.pastegroup_on = on
  439.                 changed = 1
  440.             on = self.active.can_undo()
  441.             if on <> self.undo_label:
  442.                 if on:
  443.                     self.undoitem.enable(1)
  444.                     self.undoitem.settext(on)
  445.                     self.undo_label = on
  446.                 else:
  447.                     self.undoitem.settext("Nothing to undo")
  448.                     self.undoitem.enable(0)
  449.                 changed = 1
  450.             if self.updatefontmenus():
  451.                 changed = 1
  452.         if changed:
  453.             DrawMenuBar()
  454.             
  455.     def updatefontmenus(self):
  456.         info = self.active.getruninfo()
  457.         if info == self.ffs_values:
  458.             return 0
  459.         # Remove old checkmarks
  460.         if self.ffs_values == ():
  461.             self.ffs_values = (None, None, None)
  462.         font, face, size = self.ffs_values
  463.         if font <> None:
  464.             fnum = self.fontnames.index(font)
  465.             self.fontitems[fnum].check(0)
  466.         if face <> None:
  467.             for i in range(len(self.faceitems)):
  468.                 if face & (1<<i):
  469.                     self.faceitems[i].check(0)
  470.         if size <> None:
  471.             for i in range(len(self.sizeitems)):
  472.                 if SIZES[i] == size:
  473.                     self.sizeitems[i].check(0)
  474.                 
  475.         self.ffs_values = info
  476.         # Set new checkmarks
  477.         font, face, size = self.ffs_values
  478.         if font <> None:
  479.             fnum = self.fontnames.index(font)
  480.             self.fontitems[fnum].check(1)
  481.         if face <> None:
  482.             for i in range(len(self.faceitems)):
  483.                 if face & (1<<i):
  484.                     self.faceitems[i].check(1)
  485.         if size <> None:
  486.             for i in range(len(self.sizeitems)):
  487.                 if SIZES[i] == size:
  488.                     self.sizeitems[i].check(1)
  489.         # Set outline/normal for sizes
  490.         if font:
  491.             exists = getfontsizes(font, SIZES)
  492.             for i in range(len(self.sizeitems)):
  493.                 if exists[i]:
  494.                     self.sizeitems[i].setstyle(0)
  495.                 else:
  496.                     self.sizeitems[i].setstyle(8)
  497.  
  498.     #
  499.     # Apple menu
  500.     #
  501.     
  502.     def do_about(self, id, item, window, event):
  503.         EasyDialogs.Message("A simple single-font text editor based on WASTE")
  504.             
  505.     #
  506.     # File menu
  507.     #
  508.  
  509.     def open(self, *args):
  510.         self._open(0)
  511.         
  512.     def openfile(self, *args):
  513.         self._open(1)
  514.  
  515.     def _open(self, askfile):
  516.         if askfile:
  517.             fss, ok = macfs.StandardGetFile('TEXT')
  518.             if not ok:
  519.                 return
  520.             path = fss.as_pathname()
  521.             name = os.path.split(path)[-1]
  522.             try:
  523.                 fp = open(path, 'rb') # NOTE binary, we need cr as end-of-line
  524.                 data = fp.read()
  525.                 fp.close()
  526.             except IOError, arg:
  527.                 EasyDialogs.Message("IOERROR: "+`arg`)
  528.                 return
  529.         else:
  530.             path = None
  531.             name = "Untitled %d"%self.num
  532.             data = ''
  533.         w = WasteWindow(self)
  534.         w.open(path, name, data)
  535.         self.num = self.num + 1
  536.         
  537.     def closewin(self, *args):
  538.         if self.active:
  539.             self.active.close()
  540.         else:
  541.             EasyDialogs.Message("No active window?")
  542.         
  543.     def save(self, *args):
  544.         if self.active:
  545.             self.active.menu_save()
  546.         else:
  547.             EasyDialogs.Message("No active window?")
  548.         
  549.     def saveas(self, *args):
  550.         if self.active:
  551.             self.active.menu_save_as()
  552.         else:
  553.             EasyDialogs.Message("No active window?")
  554.             
  555.         
  556.     def quit(self, *args):
  557.         for w in self._windows.values():
  558.             w.close()
  559.         if self._windows:
  560.             return
  561.         raise self
  562.         
  563.     #
  564.     # Edit menu
  565.     #
  566.     
  567.     def undo(self, *args):
  568.         if self.active:
  569.             self.active.menu_undo()
  570.         else:
  571.             EasyDialogs.Message("No active window?")
  572.         
  573.     def cut(self, *args):
  574.         if self.active:
  575.             self.active.menu_cut()
  576.         else:
  577.             EasyDialogs.Message("No active window?")
  578.         
  579.     def copy(self, *args):
  580.         if self.active:
  581.             self.active.menu_copy()
  582.         else:
  583.             EasyDialogs.Message("No active window?")
  584.         
  585.     def paste(self, *args):
  586.         if self.active:
  587.             self.active.menu_paste()
  588.         else:
  589.             EasyDialogs.Message("No active window?")
  590.  
  591.     def clear(self, *args):
  592.         if self.active:
  593.             self.active.menu_clear()
  594.         else:
  595.             EasyDialogs.Message("No active window?")
  596.         
  597.     #
  598.     # Other stuff
  599.     #    
  600.  
  601.     def idle(self, event):
  602.         if self.active:
  603.             self.active.do_idle(event)
  604.             
  605. def getfontnames():
  606.     names = []
  607.     for i in range(256):
  608.         n = Fm.GetFontName(i)
  609.         if n: names.append(n)
  610.     return names
  611.     
  612. def getfontsizes(name, sizes):
  613.     exist = []
  614.     num = Fm.GetFNum(name)
  615.     for sz in sizes:
  616.         if Fm.RealFont(num, sz):
  617.             exist.append(1)
  618.         else:
  619.             exist.append(0)
  620.     return exist
  621.  
  622. def main():
  623.     App = Wed()
  624.     App.mainloop()
  625.     
  626. if __name__ == '__main__':
  627.     main()
  628.     
  629.